#include "event_handle.h"

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>

#define EPOLL_EVENTS_MAX 1024

#define list_init(head) \
 do { \
  (head)->next = head; \
  (head)->prev = head; \
 } while (0)

#define list_append(head, node) \
 do { \
  (node)->prev = (head)->prev; \
  (node)->next = (head); \
  (node)->prev->next = (node); \
  (node)->next->prev = (node); \
 } while (0);

#define list_remove(node) \
 do { \
  (node)->prev->next = (node)->next; \
  (node)->next->prev = (node)->prev; \
 } while (0);

#define list_empty(head) ((head)->next == (head))

int event_loop_init(struct event_loop *loop)
{
    if (loop->on_event_epollin == NULL || loop->on_event_epollout == NULL)
        return -1;

    list_init(&loop->list);

    loop->fd = epoll_create1(0);
    if (loop->fd == -1) {
        perror("epoll_create1");
        return -1;
    }

    loop->running = 1;
    return 0;
}

int event_loop_stop(struct event_loop *loop)
{
    list_init(&loop->list);
    return 0;
}

int event_loop_run(struct event_loop *loop)
{
    int nfds;
    struct epoll_event events[EPOLL_EVENTS_MAX];

    while (loop->running) {
        nfds = epoll_wait(loop->fd, events, EPOLL_EVENTS_MAX, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            continue;
        }

        for (int i = 0; i < nfds; i++) {
            if (events[i].events & EPOLLIN) {
                // data can be read, callback
                loop->on_event_epollin((struct event_handle *)events[i].data.ptr);
            } else if (events[i].events & EPOLLOUT) {
                // data can be write, callback
                loop->on_event_epollout((struct event_handle *)events[i].data.ptr);
            } else if (events[i].events & EPOLLRDHUP) {
            } else {
                printf("Uncovered error\n");  // TODO: error handle
            }
        }
    }

    return 0;
}

struct event_handle *event_add(struct event_loop *loop, int sock, enum event_type type)
{
    struct event_handle *handle;
    struct epoll_event epoll_event = { 0 };

    handle = (struct event_handle *)malloc(sizeof(*handle));
    if (handle == NULL) {
        perror("event_add malloc");
        return NULL;
    }

    memset(handle, 0, sizeof(*handle));
    handle->sock = sock;
    handle->loop = loop;

    epoll_event.events = EPOLLET | ((type == EVENT_EPOLLIN) ? EPOLLIN : EPOLLOUT);
    epoll_event.data.ptr = handle;

    if (epoll_ctl(loop->fd, EPOLL_CTL_ADD, sock, &epoll_event) == -1) {
        free(handle);
        handle = NULL;
        perror("event_add epoll_ctl");
        return NULL;
    }

    list_append(&loop->list, handle);

    return handle;
}

void event_del(struct event_handle *handle)
{
    epoll_ctl(handle->loop->fd, EPOLL_CTL_DEL, handle->sock, NULL);
    list_remove(handle);
    free(handle);
    handle = NULL;
}

void event_mod(struct event_handle *handle, bool enable, enum event_type type)
{
}
